์ž‘์„ฑ: 2026-03-04 05:39:06์ˆ˜์ •: 2026-03-04 05:39:06

Next.js ์™ธ๋ถ€ ์ด๋ฏธ์ง€ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ๋ฐ ์ตœ์ ํ™” ์™„๋ฒฝ ๊ฐ€์ด๋“œ

Next.js์˜ next/image๋Š” ์ด๋ฏธ์ง€๋ฅผ ์ž๋™์œผ๋กœ ๋ฆฌ์‚ฌ์ด์ง•ํ•˜๊ณ  ์ตœ์ ํ™”ํ•ด์ฃผ๋Š” ๊ฐ•๋ ฅํ•œ ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์™ธ๋ถ€ URL์˜ ์ด๋ฏธ์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋ ค๋ฉด ๋ณด์•ˆ์„ ์œ„ํ•œ ์ถ”๊ฐ€ ์„ค์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.


1. ์™œ ๋ณด์•ˆ ์„ค์ •์ด ํ•„์š”ํ•œ๊ฐ€์š”?

์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ์•…์˜์ ์ธ URL์„ ํ†ตํ•ด ์šฐ๋ฆฌ ์„œ๋ฒ„์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ๋‚ญ๋น„ํ•˜๊ฑฐ๋‚˜, ์ด๋ฏธ์ง€ ์ตœ์ ํ™” ์„œ๋ฒ„๋ฅผ ๊ณต๊ฒฉํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ—ˆ์šฉ๋œ ๋„๋ฉ”์ธ(Hostname)๋งŒ ์ด๋ฏธ์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ด€๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


2. remotePatterns ์„ค์ • (next.config.js)

๊ฐ€์žฅ ์œ ์—ฐํ•˜๊ณ  ์•ˆ์ „ํ•œ ๋ฐฉ์‹์ธ remotePatterns๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

// next.config.js
module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'images.unsplash.com', // ํŠน์ • ํ˜ธ์ŠคํŠธ ํ—ˆ์šฉ
        port: '',
        pathname: '/**', // ๋ชจ๋“  ๊ฒฝ๋กœ ํ—ˆ์šฉ
      },
      {
        protocol: 'https',
        hostname: '**.githubusercontent.com', // ์„œ๋ธŒ๋„๋ฉ”์ธ ์™€์ผ๋“œ์นด๋“œ ํ—ˆ์šฉ
      },
    ],
  },
}

3. ์‹ค์ „ ํ™œ์šฉ ์ปดํฌ๋„ŒํŠธ ์˜ˆ์‹œ

์™ธ๋ถ€ ์ด๋ฏธ์ง€์˜ ํฌ๊ธฐ๋ฅผ ๋ฏธ๋ฆฌ ์•Œ ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ fill ์†์„ฑ์„ ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ด ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

๊ณตํ†ต ์ด๋ฏธ์ง€ ์ปดํฌ๋„ŒํŠธ (RemoteImage.tsx)

import Image from 'next/image'
 
interface Props {
  src: string;
  alt: string;
}
 
export default function RemoteImage({ src, alt }: Props) {
  return (
    <div className="relative aspect-video w-full overflow-hidden rounded-xl">
      <Image
        src={src}
        alt={alt}
        fill // ๋ถ€๋ชจ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๊ฐ€๋“ ์ฑ„์›€
        className="object-cover" // CSS์˜ background-size: cover ์—ญํ• 
        sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
        priority // ์ค‘์š”ํ•œ ์ด๋ฏธ์ง€๋Š” ์šฐ์„ ์ˆœ์œ„ ๋กœ๋“œ
      />
    </div>
  )
}

4. ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ

  1. sizes ์†์„ฑ ํ™œ์šฉ: ๋ธŒ๋ผ์šฐ์ € ํ™˜๊ฒฝ์— ๋งž๋Š” ์ตœ์  ํฌ๊ธฐ์˜ ์ด๋ฏธ์ง€๋ฅผ ์š”์ฒญํ•˜๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค. ์„ค์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋“  ๊ธฐ๊ธฐ์—์„œ ํฐ ์ด๋ฏธ์ง€๋ฅผ ๋‹ค์šด๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. placeholder="blur": ์ด๋ฏธ์ง€๊ฐ€ ๋กœ๋“œ๋˜๊ธฐ ์ „ ๋ถ€๋“œ๋Ÿฌ์šด ๋ธ”๋Ÿฌ ํšจ๊ณผ๋ฅผ ๋ณด์—ฌ์ฃผ์–ด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(UX)์„ ๊ฐœ์„ ํ•ฉ๋‹ˆ๋‹ค. (์™ธ๋ถ€ ์ด๋ฏธ์ง€๋Š” ๋ณ„๋„์˜ blurDataURL์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.)
  3. priority: LCP(Largest Contentful Paint)์— ํ•ด๋‹นํ•˜๋Š” ๋ฉ”์ธ ๋ฐฐ๋„ˆ ์ด๋ฏธ์ง€ ๋“ฑ์—๋Š” ๋ฐ˜๋“œ์‹œ priority ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

5. ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ ํ•ด๊ฒฐ

  • Error: Invalid src prop: next.config.js์— ๋„๋ฉ”์ธ์„ ๋“ฑ๋กํ•˜์ง€ ์•Š์•˜์„ ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์„ค์ •์„ ์ˆ˜์ •ํ•œ ํ›„์—๋Š” ๋ฐ˜๋“œ์‹œ ์„œ๋ฒ„๋ฅผ ์žฌ์‹œ์ž‘ํ•ด์•ผ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ์ด๋ฏธ์ง€ ๊นจ์ง: ์™ธ๋ถ€ ์„œ๋ฒ„์—์„œ Referer ์ฒดํฌ๋ฅผ ํ•˜๊ฑฐ๋‚˜ ์ด๋ฏธ์ง€๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ์ด๋Ÿด ๋•Œ๋Š” onError ํ•ธ๋“ค๋Ÿฌ๋ฅผ ํ†ตํ•ด ๊ธฐ๋ณธ(Fallback) ์ด๋ฏธ์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๋„๋ก ๊ตฌํ˜„ํ•˜์„ธ์š”.

6. ๊ฒฐ๋ก 

์™ธ๋ถ€ ์ด๋ฏธ์ง€๋ฅผ ๋‹ค๋ฃฐ ๋•Œ๋Š” ๋ณด์•ˆ ์„ค์ •์„ ์ฒ ์ €ํžˆ ํ•˜๊ณ , fill๊ณผ sizes๋ฅผ ์ ์ ˆํžˆ ์กฐํ•ฉํ•˜์—ฌ ์„ฑ๋Šฅ๊ณผ ๋””์ž์ธ์„ ๋™์‹œ์— ์žก์œผ์„ธ์š”. Next.js๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ž๋™ ์ตœ์ ํ™” ๊ธฐ๋Šฅ๋งŒ ์ž˜ ํ™œ์šฉํ•ด๋„ ์›น ์„ฑ๋Šฅ ์ ์ˆ˜(Lighthouse)๋ฅผ ํš๊ธฐ์ ์œผ๋กœ ์˜ฌ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.